package com.nuc.seckill.service.impl;

import com.google.common.util.concurrent.RateLimiter;
import com.nuc.seckill.base.*;
import com.nuc.seckill.dao.SeckillOrderDao;
import com.nuc.seckill.dao.SeckillProductsDao;
import com.nuc.seckill.dao.SeckillUserDao;
import com.nuc.seckill.exception.ErrorMessage;
import com.nuc.seckill.model.SeckillOrder;
import com.nuc.seckill.model.SeckillProducts;
import com.nuc.seckill.model.SeckillUser;
import com.nuc.seckill.service.SeckillService;
import com.nuc.seckill.utils.DecrCacheStockUtil;
import com.nuc.seckill.utils.DistrubuteLimitUtil;
import com.nuc.seckill.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author 石一歌
 * @date 2022-08-10 00:01
 */
@Service
@Slf4j
public class SeckillServiceImpl implements SeckillService {
    RateLimiter rateLimiter = RateLimiter.create(5);

    @Resource
    private SeckillOrderDao seckillOrderDao;
    @Resource
    private SeckillProductsDao seckillProductsDao;
    @Resource
    private SeckillUserDao seckillUserDao;
    @Resource
    private DecrCacheStockUtil decrCacheStockUtil;
    @Resource
    private DistrubuteLimitUtil distrubuteLimitUtil;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private Redisson redisson;

    /**
     * 原始下单
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/10 20:48
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> sOrder(SeckillReq req) {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用秒杀接口]===");
        //参数校验
        log.info("===[校验用户信息及商品信息]===");
        BaseResponse<?> paramValidRes = validateParam(productId, userId);
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验参数是否合法][通过]===");
        //下单校验
        log.info("===[校验 用户是否重复下单]===");
        SeckillOrder param = new SeckillOrder();
        param.setProductId(productId);
        param.setUserId(userId);
        int repeatCount = Math.toIntExact(seckillOrderDao.count(param));
        if (repeatCount > 0) {
            log.error("===[该用户重复下单！]===");
            return BaseResponse.error(ErrorMessage.REPEAT_ORDER_ERROR);
        }
        log.info("===[校验 用户是否重复下单][通过校验]===");
        // 扣减库存
        log.info("===[开始扣减库存]===");
        seckillProductsDao.decrStock(productId);
        log.info("===[扣减库存][成功]===");
        // 创建订单
        log.info("===[开始创建订单]===");
        insertOrder(productId, userId);
        log.info("===[创建订单][成功]===");
        return BaseResponse.OK;
    }

    /**
     * 悲观锁
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/10 20:59
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> pOrder(SeckillReq req) {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用秒杀接口(悲观锁)]===");
        //参数校验
        log.info("===[校验用户信息及商品信息]===");
        BaseResponse<?> paramValidRes = validateParamPessimistic(productId);
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验参数是否合法][通过]===");
        //下单校验
        log.info("===[校验 用户是否重复下单]===");
        List<SeckillOrder> repList = seckillOrderDao.listRepeatOrdersForUpdate(req.getUserId(), req.getProductId());
        if (repList.size() > 0) {
            log.error("===[该用户重复下单！]===");
            return BaseResponse.error(ErrorMessage.REPEAT_ORDER_ERROR);
        }
        log.info("===[校验 用户是否重复下单][通过校验]===");
        // 扣减库存
        log.info("===[开始扣减库存]===");
        seckillProductsDao.decrStock(productId);
        log.info("===[扣减库存][成功]===");
        // 创建订单
        log.info("===[开始创建订单]===");
        insertOrder(productId, userId);
        log.info("===[创建订单][成功]===");
        return BaseResponse.OK;
    }

    /**
     * 乐观锁
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/10 21:58
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> oOrder(SeckillReq req) throws Exception {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用秒杀接口(乐观锁)]===");
        //参数校验
        log.info("===[校验参数是否合法]===");
        BaseResponse<?> paramValidRes = validateParam(productId, userId);
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验参数是否合法][通过]===");
        return createOptimisticOrder(productId, userId);
    }

    /**
     * 创建订单
     *
     * @param productId 产品id
     * @param userId    用户id
     * @return com.nuc.seckill.base.BaseResponse
     * @author 石一歌
     * @date 2022/8/14 22:12
     */
    private BaseResponse<?> createOptimisticOrder(Long productId, Long userId) throws Exception {
        // 创建订单
        log.info("===[开始创建订单]===");
        insertOrder(productId, userId);
        log.info("===[创建订单][成功]===");
        //扣减库存
        log.info("===[开始扣减库存]===");
        int res = seckillProductsDao.updateStockByOptimistic(productId);
        if (res == 0) {
            log.error("===[秒杀失败，抛出异常，执行回滚逻辑！]===");
            throw new Exception("库存不足");
        }
        log.info("===[扣减库存][成功]===");
        //添加订单缓存
        try {
            addOrderedUserCache(productId, userId);
        } catch (Exception e) {
            log.error("===[记录已购用户缓存时发生异常！]===", e);
        }
        return BaseResponse.OK;
    }

    /**
     * redis缓存
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/11 16:58
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> cOrder(SeckillReq req) throws Exception {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        SeckillProducts product = seckillProductsDao.selectByPrimaryKey(productId);
        redisUtil.sSetNx("sk:sc:" + productId, product.getCount() - product.getSaled());
        log.info("===[开始调用下单接口(redis缓存)]===");
        long res = 0;
        try {
            //校验用户信息、商品信息、库存信息
            log.info("===[校验用户信息、商品信息、库存信息]===");
            BaseResponse<?> paramValidRes = validateParam(productId, userId);
            if (paramValidRes.getCode() != 0) {
                return paramValidRes;
            }
            log.info("===[校验][通过]===");

            //redis + lua
            res = decrCacheStockUtil.decrStock(productId);
            if (res == 2) {
                // 扣减完的库存只要大于等于0，就说明扣减成功
                // 开始数据库扣减库存逻辑
                log.info("===[开始扣减库存]===");
                seckillProductsDao.decrStock(productId);
                log.info("===[扣减库存][成功]===");
                log.info("===[开始创建订单]===");
                insertOrder(productId, userId);
                log.info("===[创建订单][成功]===");
                return BaseResponse.OK;
            } else {
                log.error("===[缓存扣减库存不足！]===");
                return BaseResponse.error(ErrorMessage.STOCK_NOT_ENOUGH);
            }
        } catch (Exception e) {
            log.error("===[异常！]===", e);
            if (res == 2) {
                decrCacheStockUtil.addStock(productId);
            }
            throw new Exception("异常！");
        }
    }

    /**
     * redis分布式锁
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/11 22:44
     */
    @Override
    public BaseResponse<?> rOrder(SeckillReq req) {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用下单接口(redission)]===");
        String lockKey = String.format(Constant.RedisKey.SECKILL_DISTRIBUTED_LOCK, productId);
        RLock lock = redisson.getLock(lockKey);
        try {
            //将锁过期时间设置为30s，定时任务每隔10秒执行续锁操作
            lock.lock();
            //参数校验
            log.info("===[校验用户信息及商品信息]===");
            BaseResponse<?> paramValidRes = validateParam(productId, userId);
            if (paramValidRes.getCode() != 0) {
                return paramValidRes;
            }
            log.info("===[校验参数是否合法][通过]===");
            // 扣减库存
            log.info("===[开始扣减库存]===");
            seckillProductsDao.decrStock(productId);
            log.info("===[扣减库存][成功]===");
            // 创建订单
            log.info("===[开始创建订单]===");
            insertOrder(productId, userId);
            log.info("===[创建订单][成功]===");
            return BaseResponse.OK;
        } catch (Exception e) {
            log.error("===[异常！]===", e);
            return BaseResponse.error(ErrorMessage.SYS_ERROR);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 下单v1
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/14 22:36
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> orderV1(SeckillReq req) throws Exception {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用下单接口（应用限流）]===");
        log.info("===[开始经过限流程序]===");
        //  非阻塞式获取令牌
        //if (!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
        //    log.info("请求已被限流，请稍后再试！");
        //    return BaseResponse.error(ErrorMessage.SECKILL_RATE_LIMIT_ERROR);
        //}
        try {
            if (!distrubuteLimitUtil.exec()) {
                log.info("请求已被限流，请稍后再试！");
                return BaseResponse.error(ErrorMessage.SECKILL_RATE_LIMIT_ERROR);
            }
        } catch (IOException e) {
            log.error("===[分布式限流程序发生异常！]===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        }
        log.info("===[限流程序][通过]===");
        //校验用户信息、商品信息、库存信息
        log.info("===[校验用户信息、商品信息、库存信息]===");
        BaseResponse<?> paramValidRes = validateParam(productId, userId);
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验][通过]===");
        //下单（乐观锁）
        return createOptimisticOrder(productId, userId);
    }

    /**
     * 下单v2
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/14 22:37
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> orderV2(SeckillReqV2 req) throws Exception {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用下单接口（应用限流）]===");
        log.info("===[开始经过限流程序]===");
        //分布式限流
        try {
            if (!distrubuteLimitUtil.exec()) {
                log.info("你被分布式锁限流了！直接返回失败！");
                return BaseResponse.error(ErrorMessage.SECKILL_RATE_LIMIT_ERROR);
            }
        } catch (IOException e) {
            log.error("===[分布式限流程序发生异常！]===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        }
        log.info("===[限流程序][通过]===");
        //校验用户信息、商品信息、库存信息
        log.info("===[校验用户信息、商品信息、库存信息]===");
        BaseResponse<?> paramValidRes = validateParamV2(productId, userId, req.getVerifyCode());
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验][通过]===");
        //下单（乐观锁）
        return createOptimisticOrder(productId, userId);
    }

    /**
     * 下单v3
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/15 15:40
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> orderV3(SeckillReqV3 req) throws Exception {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用下单接口（应用限流）]===");
        log.info("===[开始经过限流程序]===");
        //分布式限流
        try {
            if (!distrubuteLimitUtil.exec()) {
                log.info("你被分布式锁限流了！直接返回失败！");
                return BaseResponse.error(ErrorMessage.SECKILL_RATE_LIMIT_ERROR);
            }
        } catch (IOException e) {
            log.error("===[分布式限流程序发生异常！]===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        }
        log.info("===[限流程序][通过]===");
        //校验用户信息、商品信息、库存信息
        log.info("===[校验用户信息、商品信息、库存信息]===");
        BaseResponse<?> paramValidRes = validateParamV3(productId, userId, req.getImageId(), req.getImageCode());
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验][通过]===");
        //下单（乐观锁）
        return createOptimisticOrder(productId, userId);
    }

    /**
     * 下单v4
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/17 0:12
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> orderV4(SeckillReqV2 req) throws Exception {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用下单接口（应用限流）]===");
        log.info("===[开始经过限流程序]===");
        //分布式限流
        try {
            if (!distrubuteLimitUtil.exec()) {
                log.info("你被分布式锁限流了！直接返回失败！");
                return BaseResponse.error(ErrorMessage.SECKILL_RATE_LIMIT_ERROR);
            }
        } catch (IOException e) {
            log.error("===[分布式限流程序发生异常！]===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        }
        log.info("===[限流程序][通过]===");
        //校验用户信息、商品信息、库存信息
        log.info("===[校验用户信息、商品信息、库存信息]===");
        BaseResponse<?> paramValidRes = validateParamV4(productId, userId, req.getVerifyCode());
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验][通过]===");
        //下单（乐观锁）
        return createOptimisticOrder(productId, userId);
    }

    /**
     * 下单v5
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/20 10:49
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> orderV5(SeckillReqV2 req) throws Exception {
        log.info("===[开始调用下单接口（缓存与数据库一致性-先删库存，后更新数据库）]===");
        log.info("===[开始经过限流程序]===");
        //分布式限流
        try {
            if (!distrubuteLimitUtil.exec()) {
                log.info("你被分布式锁限流了！直接返回失败！");
                return BaseResponse.error(ErrorMessage.SECKILL_RATE_LIMIT_ERROR);
            }
        } catch (IOException e) {
            log.error("===[分布式限流程序发生异常！]===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        }
        log.info("===[限流程序][通过]===");
        //校验用户信息、商品信息、库存信息
        log.info("===[校验用户信息、商品信息、库存信息]===");
        BaseResponse<?> paramValidRes = validateParamV4(req.getProductId(), req.getUserId(), req.getVerifyCode());
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验][通过]===");
        //下单（乐观锁）
        return createOptimisticOrderV5(req.getProductId(), req.getUserId());
    }

    /**
     * 下单v6
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/20 11:00
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> orderV6(SeckillReqV2 req) throws Exception {
        log.info("===[开始调用下单接口（缓存与数据库一致性-先删库存，后更新数据库）]===");
        log.info("===[开始经过限流程序]===");
        //分布式限流
        try {
            if (!distrubuteLimitUtil.exec()) {
                log.info("你被分布式锁限流了！直接返回失败！");
                return BaseResponse.error(ErrorMessage.SECKILL_RATE_LIMIT_ERROR);
            }
        } catch (IOException e) {
            log.error("===[分布式限流程序发生异常！]===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        }
        log.info("===[限流程序][通过]===");
        //校验用户信息、商品信息、库存信息
        log.info("===[校验用户信息、商品信息、库存信息]===");
        BaseResponse<?> paramValidRes = validateParamV4(req.getProductId(), req.getUserId(), req.getVerifyCode());
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验][通过]===");
        //下单（乐观锁）
        return createOptimisticOrderV6(req.getProductId(), req.getUserId());
    }

    /**
     * 下单v8
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/9/3 17:45
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResponse<?> orderV8(SeckillReqV2 req) throws Exception {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        SeckillProducts product = seckillProductsDao.selectByPrimaryKey(productId);
        redisUtil.sSetNx("sk:sc:" + productId, product.getCount() - product.getSaled());
        log.info("===[开始调用下单接口(订单异步处理)]===");
        long res = 0;
        try {
            //校验用户信息、商品信息、库存信息
            log.info("===[校验用户信息、商品信息、库存信息]===");
            BaseResponse<?> paramValidRes = validateParam(productId, userId);
            if (paramValidRes.getCode() != 0) {
                return paramValidRes;
            }
            log.info("===[校验][通过]===");
            //redis + lua
            res = decrCacheStockUtil.decrStock(productId);
            if (res == 2) {
                // 扣减完的库存只要大于等于0，就说明扣减成功
                // 开始数据库扣减库存逻辑
                return BaseResponse.OK;
            } else {
                log.error("===[缓存扣减库存不足！]===");
                return BaseResponse.error(ErrorMessage.STOCK_NOT_ENOUGH);
            }
        } catch (Exception e) {
            log.error("===[异常！]===", e);
            decrCacheStockUtil.addStock(productId);
            throw new Exception("异常！");
        }
    }

    /***
     *异步处理订单
     * @param req 请求
     * @author 石一歌
     * @date 2022/9/3 18:49
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void asyncOrder(SeckillReq req) throws Exception {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        // 创建订单
        // 利用唯一索引，保证幂等性，一旦消费重复的消息，则此处会抛出异常
        log.info("===[开始创建订单]===");
        insertOrder(productId, userId);
        log.info("===[创建订单][成功]===");
        //扣减库存
        seckillProductsDao.decrStock(productId);
        // 将已下单用户放入缓存中
        log.info("===[开始将已下单用户放入缓存！]===");
        addOrderedUserCache(productId, userId);
    }

    /**
     * 先更新数据库 再删除缓存
     *
     * @param productId 商品id
     * @param userId    用户id
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/20 10:54
     */
    private BaseResponse<?> createOptimisticOrderV5(Long productId, Long userId) throws Exception {
        log.info("===[下单逻辑][开始]===");
        // 创建订单
        insertOrder(productId, userId);
        log.info("===[下单逻辑][创建订单成功]===");
        //扣减库存
        int res = seckillProductsDao.updateStockByOptimistic(productId);
        if (res == 0) {
            log.error("===[乐观锁并发更新失败，抛出异常，执行回滚逻辑！]===");
            throw new Exception("乐观锁并发更新失败");
        }
        log.info("===[下单逻辑][扣减库存成功]===");
        //先更新数据库 再删除缓存
        delStockByCache(productId);
        try {
            addOrderedUserCache(productId, userId);
        } catch (Exception e) {
            log.error("===[记录已购用户缓存时发生异常！]===", e);
        }
        log.info("===[下单逻辑][结束]===");
        return BaseResponse.OK;
    }

    /**
     * 先删除缓存 再更新数据库
     *
     * @param productId 商品id
     * @param userId    用户id
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/20 10:54
     */
    private BaseResponse<?> createOptimisticOrderV6(Long productId, Long userId) throws Exception {
        //先删除缓存 再更新数据库
        delStockByCache(productId);
        log.info("===[下单逻辑][开始]===");
        // 创建订单
        insertOrder(productId, userId);
        log.info("===[下单逻辑][创建订单成功]===");
        //扣减库存
        int res = seckillProductsDao.updateStockByOptimistic(productId);
        if (res == 0) {
            log.error("===[乐观锁并发更新失败，抛出异常，执行回滚逻辑！]===");
            throw new Exception("乐观锁并发更新失败");
        }
        log.info("===[下单逻辑][扣减库存成功]===");

        try {
            addOrderedUserCache(productId, userId);
        } catch (Exception e) {
            log.error("===[记录已购用户缓存时发生异常！]===", e);
        }
        log.info("===[下单逻辑][结束]===");
        return BaseResponse.OK;
    }

    /**
     * 删除缓存
     *
     * @param productId 商品id
     * @author 石一歌
     * @date 2022/8/20 11:10
     */
    @Override
    public void delStockByCache(Long productId) {
    }

    /**
     * nginx
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/16 22:45
     */
    @Override
    public BaseResponse<?> nOrder(SeckillReq req) {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        // 创建订单
        log.info("===[开始创建订单]===");
        insertOrder(productId, userId);
        log.info("===[创建订单][成功]===");
        //扣减库存
        seckillProductsDao.decrStock(productId);
        return BaseResponse.OK;
    }


    /**
     * 生成验证值
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/13 23:36
     */
    @Override
    public BaseResponse<?> getVerifyHash(SeckillReq req) {
        Long productId = req.getProductId();
        Long userId = req.getUserId();
        log.info("===[开始调用获取验证码接口]===");
        //校验用户信息、商品信息、库存信息
        log.info("===[校验用户信息、商品信息、库存信息]===");
        BaseResponse<?> paramValidRes = validateParam(productId, userId);
        if (paramValidRes.getCode() != 0) {
            return paramValidRes;
        }
        log.info("===[校验][通过]===");
        //生成hash
        String verify = Constant.VALIDATE_CODE_SALT + req.getProductId() + req.getUserId();
        String verifyHash = DigestUtils.md5DigestAsHex(verify.getBytes());
        //将hash和用户商品信息存入redis
        String key = String.format(Constant.RedisKey.SECKILL_VALIDATE_CODE, req.getProductId(), req.getUserId());
        redisUtil.set(key, verifyHash, 60);
        return BaseResponse.ok(verifyHash);
    }

    /**
     * 创建订单
     *
     * @param productId 产品
     * @param userId    用户id
     * @author 石一歌
     * @date 2022/8/11 16:59
     */
    private void insertOrder(Long productId, Long userId) {
        SeckillProducts product = seckillProductsDao.selectByPrimaryKey(productId);
        Date date = new Date();
        SeckillOrder order = new SeckillOrder();
        order.setProductId(product.getId());
        order.setProductName(product.getName());
        order.setUserId(userId);
        order.setCreateTime(date);
        seckillOrderDao.insert(order);
    }

    /**
     * 悲观锁校验
     *
     * @param productId 产品id
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/11 17:00
     */
    private BaseResponse<?> validateParamPessimistic(Long productId) {
        SeckillProducts product = seckillProductsDao.selectByPrimaryKeyForUpdate(productId);
        return validateBasic(product);
    }

    /**
     * 一般校验
     *
     * @param productId 产品id
     * @param userId    用户id
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/12 23:17
     */
    private BaseResponse<?> validateParam(Long productId, Long userId) {
        SeckillProducts product = seckillProductsDao.selectByPrimaryKey(productId);
        validateBasic(product);
        if (hasOrderedUserCache(productId, userId)) {
            log.error("===[用户重复下单！]===");
            return BaseResponse.error(ErrorMessage.REPEAT_ORDER_ERROR);
        }
        return BaseResponse.OK;
    }

    /**
     * 一般校验v2
     *
     * @param productId  产品id
     * @param userId     用户id
     * @param verifyCode 验证码
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/14 21:58
     */
    private BaseResponse<?> validateParamV2(Long productId, Long userId, String verifyCode) {
        SeckillUser user = seckillUserDao.selectByPrimaryKey(userId);
        validateParam(productId, userId);
        if (user == null) {
            log.error("===[用户不存在！]===");
            return BaseResponse.error(ErrorMessage.SYS_ERROR);
        }
        //校验验证码
        String key = String.format(Constant.RedisKey.SECKILL_VALIDATE_CODE, productId, userId);
        if (!verifyCode.equals(String.valueOf(redisUtil.get(key)))) {
            return BaseResponse.error(ErrorMessage.SECKILL_VALIDATE_ERROR);
        }
        return BaseResponse.OK;
    }

    /**
     * 一般校验v3
     *
     * @param productId 产品id
     * @param userId    用户id
     * @param imageId   图片id
     * @param imageCode 图片验证码
     * @return com.nuc.seckill.base.BaseResponse
     * @author 石一歌
     * @date 2022/8/15 15:44
     */
    private BaseResponse<?> validateParamV3(Long productId, Long userId, String imageId, String imageCode) {
        SeckillUser user = seckillUserDao.selectByPrimaryKey(userId);
        validateParam(productId, userId);
        if (user == null) {
            log.error("===[用户不存在！]===");
            return BaseResponse.error(ErrorMessage.SYS_ERROR);
        }
        //校验图形验证码
        String key = String.format(Constant.RedisKey.SECKILL_IMAGE_CODE, imageId);
        if (!redisUtil.hasKey(key) || !Objects.equals(redisUtil.get(key), imageCode)) {
            redisUtil.del(key);
            log.error("===[校验图片验证码未通过！]===");
            return BaseResponse.error(ErrorMessage.SECKILL_VALIDATE_ERROR);
        }
        return BaseResponse.OK;
    }

    /**
     * 一般校验v4
     *
     * @param productId  产品id
     * @param userId     用户id
     * @param verifyCode 验证码
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/17 0:16
     */
    private BaseResponse<?> validateParamV4(Long productId, Long userId, String verifyCode) {
        SeckillUser user = seckillUserDao.selectByPrimaryKey(userId);
        validateParam(productId, userId);
        if (user == null) {
            log.error("===[用户不存在！]===");
            return BaseResponse.error(ErrorMessage.SYS_ERROR);
        }
        //单用户访问频次限制
        String visitKey = String.format(Constant.RedisKey.SECKILL_USER_VISIT, productId, userId);
        long visitCount = redisUtil.incr(visitKey, 1L);
        redisUtil.expire(visitKey, 5);
        if (visitCount > Constant.VISIT_LIMIT) {
            return BaseResponse.error(ErrorMessage.SECKILL_USER_VISIT_LIMIT_ERROR);
        }
        log.info("===[单用户频次限制合法]===");
        //校验验证码
        String key = String.format(Constant.RedisKey.SECKILL_VALIDATE_CODE, productId, userId);
        if (!verifyCode.equals(String.valueOf(redisUtil.get(key)))) {
            return BaseResponse.error(ErrorMessage.SECKILL_VALIDATE_ERROR);
        }
        return BaseResponse.OK;
    }

    /**
     * 检验内容
     *
     * @param product 产品
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/11 17:01
     */
    private BaseResponse<?> validateBasic(SeckillProducts product) {
        if (product == null) {
            log.error("===[产品不存在！]===");
            return BaseResponse.error(ErrorMessage.SYS_ERROR);
        }
        if (product.getStartBuyTime().getTime() > System.currentTimeMillis()) {
            log.error("===[秒杀还未开始！]===");
            return BaseResponse.error(ErrorMessage.SECKILL_NOT_START);
        }
        if (product.getSaled() >= product.getCount()) {
            log.error("===[库存不足！]===");
            return BaseResponse.error(ErrorMessage.STOCK_NOT_ENOUGH);
        }
        return BaseResponse.OK;
    }

    /**
     * 添加订单用户缓存
     *
     * @param productId 产品id
     * @param userId    用户id
     * @author 石一歌
     * @date 2022/8/12 23:45
     */
    public void addOrderedUserCache(Long productId, Long userId) {
        String key = String.format(Constant.RedisKey.SECKILL_ORDERED_USER, productId);
        redisUtil.sSet(key, userId);
        log.info("===[已将已购用户放入缓存！]===");
    }

    /**
     * 订单用户缓存判断
     *
     * @param productId 产品id
     * @param userId    用户id
     * @return java.lang.Boolean
     * @author 石一歌
     * @date 2022/8/12 23:46
     */
    @Override
    public Boolean hasOrderedUserCache(Long productId, Long userId) {
        String key = String.format(Constant.RedisKey.SECKILL_ORDERED_USER, productId);
        return redisUtil.sHasKey(key, userId);
    }

    /**
     * 获取数据源库存
     *
     * @param productId 商品id
     * @return java.lang.Integer
     * @author 石一歌
     * @date 2022/8/20 10:29
     */
    @Override
    public Integer getStockByDataSource(Long productId) {
        SeckillProducts products = seckillProductsDao.selectByPrimaryKey(productId);
        return products.getCount() - products.getSaled();
    }

    /**
     * 获取缓存库存
     *
     * @param productId 商品id
     * @return java.lang.Integer
     * @author 石一歌
     * @date 2022/8/20 10:30
     */
    @Override
    public Integer getStockByCache(Long productId) {
        String key = String.format(Constant.RedisKey.SECKILL_SALED_COUNT, productId);
        Object count = redisUtil.get(key);
        if (count == null) {
            count = getStockByDataSource(productId);
            redisUtil.set(key, count);
        }
        return Integer.valueOf(String.valueOf(count));
    }
}
